unit oScene;

interface
  uses oProject, oLevel, Generics.Collections, oIStoreable, XMLIntf, oWorldConfiguration,
  SysUtils, oEntity, Classes, oPhysicalEntity;

  type


    TScene = class
      private
        aLevels : TList<TLevel>;
      public
        constructor Create; overload;
        procedure addLevel(paLevel : TLevel); overload;
        function addLevel(paName : string) : TLevel; overload;
        function addLevel(paName : string; paType : integer) : TLevel; overload;
        function addLevel(paName : string; paType : integer;
          paHeight, paWidth : real) : TLevel; overload;
        function addPhysicalEntity(paLayer : integer; paEntity : TPhysicalEntity;
             paId : integer): boolean;
        function loadLayer(paPath : string; paType : integer) : TLevel;
        function setLevel(paLevel : TLevel; paIndex : integer) : boolean;
        procedure deleteEntity(paId : integer);
        function getLevelIndex(paNme : string; paType : integer) : integer;
        function getLevel(paNme : string; paType : integer) : TLevel; overload;
        function getLevel(paIndex : integer) : TLevel;overload;
        function getLevel(paName : string) : TLevel;overload;
        function getEntityById(paId : integer) : TEntity;
        function getPhysicalEntityById(paId : integer) : TPhysicalEntity;
        procedure deleteLevel(paIndex : integer); overload;
        procedure deleteLevel(paName : string); overload;
        procedure ClearPedestrians;
        procedure savePhysLayerToXML(paName : string; var paNode : IXMLNode);
        procedure saveLogLayerToXML(paName : string; var paNode : IXMLNode);
        procedure loadFromConfig(paConfig : TWorldConfiguration; paPath : string);
        property Levels : TList<TLevel> read aLevels;
        procedure SaveToXML(paPath : string; paPhysLayers, paLogLayers : TList<TMapping>);overload;
        procedure LoadFromXML(paPath : string; paPhysLayers, paLogLayers : TList<TMapping>);overload;
        function getMinimumCellSize : real;
        function getLevelSize(paLevelIndex: Integer): Real;
    end;

implementation

{ TScene }
uses oMathematic;

procedure TScene.addLevel(paLevel: TLevel);
begin
  aLevels.Add(paLevel);
end;

function TScene.addLevel(paName: string): TLevel;
var
  level : TLevel;
begin
  level := TLevel.Create;
  level.Name := paName;
  Levels.Add(level);
  Result := level;
end;

function TScene.addLevel(paName: string; paType: integer): TLevel;
var
  level : TLevel;
begin
  level := addLevel(paName);
  level.TypeLayer := paType;
  Result := level;
end;

function TScene.addLevel(paName: string; paType: integer; paHeight,
  paWidth: real): TLevel;
var
  level : Tlevel;
begin
  level := TLevel.Create(paHeight, paWidth);
  level.TypeLayer := paType;
  level.Name := paName;
  Levels.Add(level);
  Result := level;
end;

function TScene.addPhysicalEntity(paLayer: integer;
  paEntity: TPhysicalEntity; paId : integer): boolean;
begin
  Result := false;
  if paEntity <> nil then
   begin
     paEntity.ID := paId;
     if aLevels.Count > paLayer then
      result := aLevels[paLayer].insertPhysicalEntity(paEntity);
   end;
end;

procedure TScene.ClearPedestrians;
var
  level: TLevel;
begin
  for level in Levels do
    level.AllPedestrians.Clear;
end;

constructor TScene.Create;
begin
  aLevels := TList<TLevel>.Create;
end;

procedure TScene.deleteEntity(paId: integer);
var
  level : TLevel;
begin
   for level in aLevels do
      if level.DeleteLogicalEntity(paId) then
        break;
end;

procedure TScene.deleteLevel(paName: string);
var
  j, match : integer;
begin
  match := -1;
  for j := 0 to aLevels.Count - 1 do
    if aLevels.Items[j].Name = paName then
     begin
          match := j;
     end;
  if match <> -1 then
    aLevels.Delete(match);
end;

function TScene.getEntityById(paId: integer): TEntity;
var
  level : TLevel;
  entity : TEntity;
begin
  entity := nil;
  for level in aLevels do
  begin
    entity := level.getEntity(paId);
    if entity <> nil then
      break;
  end;
  Result := entity;
end;

function TScene.getLevel(paName: string): TLevel;
var
  j : integer;
  l : TLevel;
begin
  j := 0;
  l := nil;
  while j < aLevels.Count do
    begin
      if aLevels[j].Name = paName then
        begin
          l := aLevels[j];
          j := aLevels.Count;
        end;
      Inc(j);
    end;
  Result := l;
end;

function TScene.getLevel(paNme: string; paType: integer): TLevel;
var
  ind, j : integer;
begin
  ind := -1;
  for j := 0 to aLevels.Count - 1 do
  begin
    if (alevels[j].Name = paNme) and (aLevels[j].TypeLayer = paType) then
        ind := j;
  end;
  if ind <> -1 then
    Result := aLevels[ind]
  else
    Result := nil;
end;

function TScene.getLevelIndex(paNme: string; paType : integer): integer;
var
  ind, j : integer;
begin
  ind := -1;
  for j := 0 to aLevels.Count - 1 do
  begin
    if (alevels[j].Name = paNme) and (aLevels[j].TypeLayer = paType) then
        ind := j;
  end;
  Result := ind;
end;

function TScene.getLevelSize(paLevelIndex: Integer): Real;
begin
  if paLevelIndex >= aLevels.Count
    then result := -1
    else result := aLevels.Items[paLevelIndex].SizeX;
end;

function TScene.getMinimumCellSize: real;
var
  level: TLevel;
  min : real;
begin
  min := MaxFloat;
  for level in Levels do
  begin
   if level.CellSize < min then
    min := level.CellSize;
  end;
  Result := min;
end;

function TScene.getPhysicalEntityById(paId: integer): TPhysicalEntity;
var
  level : TLevel;
  pe : TPhysicalEntity;
begin
  pe := nil;
  for level in aLevels do
  begin
    pe := level.getPhysicalEntity(paId);
    if pe <> nil then break;
  end;
  Result := pe;
end;

procedure TScene.saveLogLayerToXML(paName: string; var paNode : IXMLNode);
begin
  getLevel(paName).SaveLogToXML(paNode);
end;

procedure TScene.savePhysLayerToXML(paName: string; var paNode : IXMLNode);
begin
    getLevel(paName).SavePhysToXML(paNode);
end;

procedure TScene.loadFromConfig(paConfig: TWorldConfiguration; paPath : string);
var
  I: Integer;
  level : Tlevel;
begin
  for I := 0 to paConfig.Layers.Count - 1 do
    begin
      level := TLevel.Create;
      level.LoadFromXML(paPath + '/phys_layers/' + paConfig.Layers[i].physLayer,
       paPath + '/log_layers/' + paConfig.Layers[i].logLayer);
      addLevel(level);
    end;
end;

procedure TScene.deleteLevel(paIndex: integer);
begin
  aLevels.Delete(paIndex);
end;

function TScene.getLevel(paIndex: integer): TLevel;
begin
  if paIndex < aLevels.Count then
    Result := aLevels.Items[paIndex]
  else
    Result := nil;
end;

procedure TScene.LoadFromXML(paPath: string; paPhysLayers, paLogLayers : TList<TMapping>);
var
  I: Integer;
  level : TLevel;
begin
  aLevels.Clear;
  for I := 0 to paPhysLayers.Count - 1 do
    if (FileExists(paPath + 'phys_layers\' + paPhysLayers[i].Value + '.xml')) then
    begin
      level := TLevel.Create;
      level.LoadPhysFromXML(paPath + 'phys_layers\' + paPhysLayers[i].Value + '.xml');
      aLevels.Add(level);
    end;

  for I := 0 to paLogLayers.Count - 1 do
    if (FileExists(paPath + 'log_layers\' + paLogLayers[i].Value + '.xml')) then
     begin
      level := TLevel.Create;
      level.TypeLayer := LOG_LAYER;
      level.LoadLogFromXML(paPath + 'log_layers\' + paLogLayers[i].Value + '.xml');
      aLevels.Add(level);
    end;

end;


function TScene.loadLayer(paPath: string; paType: integer) : TLevel;
var
  level : TLevel;
begin
  level := TLevel.Create;
  if paType = PHYS_LAYER then
    level.LoadPhysFromXML(paPath)
  else
    level.LoadLogFromXML(paPath);
  aLevels.Add(level);
  Result := level;
end;

procedure TScene.SaveToXML(paPath: string; paPhysLayers, paLogLayers : TList<TMapping>);
var
  level : TLevel;
begin
  if DirectoryExists(paPath) then
   begin
     if not DirectoryExists(paPath + '\phys_layers') then
            MkDir(paPath + '\phys_layers');
     if not DirectoryExists(paPath + '\log_layers') then
            MkDir(paPath + '\log_layers');

{     for map in paPhysLayers do
     begin
       match := false;
       for level in aLevels do
        begin
          if level.Name = map.Key then
             match := true;
        end;
        if not match then
          begin
            new := TLevel.Create;
            new.Name := map.Key;
            new.SavePhysToXML(paPath + '\phys_layers\' +
          map.Value + '.xml');
          end;
     end;

     for map in paLogLayers do
     begin
       match := false;
       for level in aLevels do
        begin
          if level.Name = map.Key then
             match := true;
        end;
        if not match then
          begin
            new := TLevel.Create;
            new.TypeLayer := LOG_LAYER;
            new.Name := map.Key;
            new.SaveLogToXML(paPath + '\log_layers\' +
          map.Value + '.xml');
          end;
     end; }

    for level in aLevels do
      begin
        if level.TypeLayer = PHYS_LAYER then
         begin
          level.SavePhysToXML(paPath + '\phys_layers\' +
          TProject.getFileName(level.Name, paPhysLayers) + '.xml');
         end
        else
         begin
          level.SaveLogToXML(paPath + '\log_layers\' +
          TProject.getFileName(level.Name, paLogLayers) + '.xml');
         end;
      end;
   end;
end;

function TScene.setLevel(paLevel: TLevel; paIndex: integer): boolean;
begin
  if (aLevels.Count > paIndex) then
    begin
      aLevels.Items[paIndex] := paLevel;
      Result := true;
    end
  else
    Result := false;
end;

end.


